using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using AutoMapper;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json.Serialization;
using Serilog;
using Wuther.Bussiness.Interface;
using Wuther.Bussiness.Service;
using Wuther.Bussiness.Service.Autofac;
using Wuther.Entities.Models;
using Wuther.Util.PropertyMapping;

namespace Wuther.Api
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        public ILifetimeScope AutofacContainer { get; private set; }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddResponseCaching();
            var conString = Configuration["ConnectionStrings:DefaultConnection"];
            var identityServerAddress = Configuration["ConnectionStrings:IdentityServer4Url"];
            services.AddAuthentication("Bearer")
            .AddJwtBearer("Bearer", options =>
            {
                options.Authority = identityServerAddress;   //IdentityServer的地址
                options.RequireHttpsMetadata = false;

                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateAudience = false
                };
            });

            services.AddAuthorization(option =>
            {
                option.AddPolicy("ApiScope", policy =>
                {
                    policy.RequireAuthenticatedUser();
                    policy.RequireClaim("scope", "api1");
                });
            });
            //这里将OpenApi Json 信息生成服务添加到依赖注入容器中，负责根据web api 的定义生成相应的OpenApi Json 信息。
            services.AddSwaggerGen(options =>
            {
                //这里我们将菜单相关的API分成一组,这里的User就是文档名称(documentName)
                options.SwaggerDoc("Menu", new Microsoft.OpenApi.Models.OpenApiInfo()
                {
                    Title = "菜单管理",
                    Version = "1.0"
                });
                options.SwaggerDoc("Blog", new Microsoft.OpenApi.Models.OpenApiInfo()
                {
                    Title = "博客管理",
                    Version = "1.0"
                });
                options.SwaggerDoc("Article", new Microsoft.OpenApi.Models.OpenApiInfo()
                {
                    Title = "文章管理",
                    Version = "1.0"
                });
                options.SwaggerDoc("Calligraphy", new Microsoft.OpenApi.Models.OpenApiInfo()
                {
                    Title = "书法",
                    Version = "1.0"
                });
                options.SwaggerDoc("UploadFile", new Microsoft.OpenApi.Models.OpenApiInfo()
                {
                    Title = "文件上传",
                    Version = "1.0"
                });
                //以下是设置SwaggerUI中所有Web Api 的参数注释、返回值等信息从哪里获取。
                var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
                var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
                options.IncludeXmlComments(xmlPath);
            });
            services.AddControllers(setup =>
            {
                setup.ReturnHttpNotAcceptable = true;
                setup.CacheProfiles.Add("120sCacheProfile", new CacheProfile()
                {
                    Duration = 120
                });
                //setup.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
                //setup.OutputFormatters.Insert(0, new XmlDataContractSerializerOutputFormatter());
            })
            .AddNewtonsoftJson(setup =>
            {
                setup.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
                setup.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
            })
            .AddXmlDataContractSerializerFormatters()
            .ConfigureApiBehaviorOptions(setup =>
            {
                setup.InvalidModelStateResponseFactory = context =>
                {
                    var problemDetails = new ValidationProblemDetails(context.ModelState)
                    {
                        Type = "http://baidu.com",
                        Title = "出错啦",
                        Status = StatusCodes.Status422UnprocessableEntity,
                        Detail = "请查看详细信息",
                        Instance = context.HttpContext.Request.Path
                    };
                    problemDetails.Extensions.Add("traceId", context.HttpContext.TraceIdentifier);
                    return new UnprocessableEntityObjectResult(problemDetails)
                    {
                        ContentTypes = { "application/problem+json" }
                    };
                };
            });

            services.Configure<MvcOptions>(config =>
            {
                var newtonSoftJsonOutputFormatter = config.OutputFormatters.OfType<NewtonsoftJsonOutputFormatter>()
                ?.FirstOrDefault();
                newtonSoftJsonOutputFormatter?.SupportedMediaTypes.Add("application/vnd.wuther.hateoas+json");
            });
            services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
            services.AddCors();
            services.AddDbContext<wutherContext>(options => options.UseMySql(conString));
            services.AddTransient<DbContext, wutherContext>();
            services.AddTransient(typeof(IRepository<>), typeof(Repository<>));
            //services.AddTransient<IUserRepository, UserRepository>();
            services.AddTransient<IMenuRepository, MenuRepository>();
            services.AddTransient<IBlogsRepository, BlogsRepository>();
            services.AddTransient<IPropertyMappingService, PropertyMappingService>();
            services.AddTransient<IPropertyCheckerService, PropertyCheckerService>();
        }
        public void ConfigureContainer(ContainerBuilder builder)
        {
            // Register your own things directly with Autofac, like:
            builder.RegisterModule(new AutofacModule());
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler(appBuilder =>
                {
                    appBuilder.Run(async context =>
                    {
                        context.Response.StatusCode = 500;
                        await context.Response.WriteAsync("Unexpected Error");
                    });
                });
            }
            this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();

            //todo:测试可以允许任意跨域，正式环境要加权限
            app.UseCors(builder => builder.AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader());

            app.UseHttpsRedirection();

            app.UseResponseCaching();

            app.UseSerilogRequestLogging();

            app.UseSwagger();

            //UseSwaggerUI 添加了一个中间件，主要用于拦截SwaggerUI界面的访问请求，并根据OpenApi Json 信息动态生成接口列表，在上面的基本原理讲述中，默认请求SwaggerUI 界面的http地址是：http://localhost:5000/swagger/index.html
            app.UseSwaggerUI((options) =>
            {
                //SwaggerEndPoint 方法用于告诉SwaggerUI 请求哪个地址来获取OpenApi Json 信息，后面我们会讲解如何自定义这个路径。
                //options.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
                //这里告诉SwaggerUI从/WutherApi/v1/swagger.json 获取 OpenApi Json 信息。
                //options.SwaggerEndpoint("/WutherApi/v1/swagger.json", "WutherApi");

                //定义菜单管理相关API的OpenApi Json 信息请求路径。
                options.SwaggerEndpoint("/WutherApi/Menu/swagger.json", "MenuApi");
                options.SwaggerEndpoint("/WutherApi/Blog/swagger.json", "BlogApi");
                options.SwaggerEndpoint("/WutherApi/Article/swagger.json", "ArticleApi");
                options.SwaggerEndpoint("/WutherApi/Calligraphy/swagger.json", "CalligraphyApi");
                options.SwaggerEndpoint("/WutherApi/UploadFile/swagger.json", "UploadFileApi");

                //这里我们告诉SwaggerUI 请求拦截中间件，当收到浏览器发送过来的/BlogApisDocs 的请求则返回SwaggerUI 页面。
                //options.RoutePrefix = "WutherApiDocs";
            });
            //配置拦截OpenAPI Json 信息的中间件，重新定义OpenApi Json信息的请求路径格式
            app.UseSwagger(swaggerOptions =>
            {
                //告诉OpenApi Json 信息请求拦截中间件，收到以下格式的请求时返回OpenApi Json信息
                //注意：路径中必须包含: {documentName}，Swagger 中间件从请求路径中{documentName}占位符位置提取出api 文档名称，以便显示分组到该文档名称下的 
                //所有api信息。
                swaggerOptions.RouteTemplate = "/WutherApi/{documentName}/swagger.json";
            });

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                //.RequireAuthorization("ApiScope");
            });
        }
    }
}
